注意:所有文章除特别说明外,转载请注明出处.
集合
[TOC]
注意:Object类型的数组可以存储任意类型的数据。
1.集合
集合是存储对象数据的容器。
1.集合比数组的优势:
1.集合可以存储任意类型的数据,数组只能存储同一种数据类型的数据(Object类型的数组除外)。
2.集合的长度是会发生变化的,数组的长度是固定的。
2.集合类的继承体系:
Collection:是所有单例集合的根接口,其有两个接口List/Set
Set:如果实现了Set接口的集合类,具备特点:无序、不可重复。
List:如果实现了List接口的集合类,具备的特点:有序、可重复。有序:集合的有序不是指自然顺序,而是指添加进去的顺序与元素出来的顺序一致。
提示:Collection与Collections的区别在于Collection是一个集合接口,其提供了对集合对象进行操作的通用接口方法。实现接口类主要有List/Set,该接口的设计是为各种具体的集合提供最大化的统一操作方式。Collections是针对集合类的一个包装类,它提供了一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作。
继承:1.父类定义完整的成员、静态成员、非静态成员、构造方法,静态变量和静态方法都可以通过子类名.父类静态成员的形式调用。2.所有的私有成员不能继承private修饰的成员。3.构造函数不能被继承。
super关键字
this关键字和super关键字很像,this关键字指向的是当前对象的调用,super关键字指向的是当前对象调用的父类。this/super关键字只能在有对象的前提下使用,不能在静态上下文使用。
提示:子类的构造函数默认第一行会调用父类的无参构造函数。super();
提示:在子类构造函数第一行通过super关键字调用父类任何构造函数,如果显式调用父类的构造函数,编译器自动添加的调用父类无参数的构造就消失,构造函数的调用只能放在第一行,只能调用一次。super()和this()不能同时存在构造函数的第一行。
Collection接口中的方法(因为接口Collection不可以直接实现,此时使用多态ArrayList):
增加:<br>
1.add(E e):添加成功返回true,添加失败返回false<br>
2.addAll(Collection c):把一个集合中的元素添加到另外一个集合中去。例:c2.add("王林");c.addAll(c2);//就是将c2的元素添加到c集合中去。
删除:<br>
1.clear():清空集合中的元素<br>
2.remove(o):指定集合中的元素删除,删除成功返回true,删除失败返回false<br>
3.removeAll():该方法是删除一个集合中与另一个集合的交集元素。c.removeAll(c2);//删除c集合中与c2集合中的交集元素,因为是c集合调用此方法。<br>
4.retainAll():保留c集合与c2的交集元素,其它的元素一并删除。c.retainAll(c2);//
查看:<br>
size():查看集合中元素的个数。c.size();//查看c集合中元素的个数
判断:<br>
1.isEmpty():该方法是判断集合是否为空<br>
2.contains(Object o):判断集合中是否存在指定的元素<br>
3.containsAll(Collection c):判断集合c中是否包含c2的所有元素。c.containsAll(c2);//
迭代:<br>
遍历集合中的元素的方法:<br>
方式一:使用toArray()方法 将c集合中的所有元素存储到一个Object数组中返回。<br>
方式二:使用迭代器iterator()遍历,Iterator it=c.iterator();//返回一个迭代器<br>
1.toArray():将集合中的元素全部存储到一个Object数组中返回。
2.iterator():
迭代器的作用:用于抓取集合中的元素,就类似与抓娃娃机里面的那个爪子一样。iterator实际上返回的是iterator接口的实现类对象。
迭代器的方法:<br>
1.hasNext():问是否存在下一个元素,如果有元素则返回true,否则返回false。(当前指针是否有指向元素,如果有返回true,否则返回false)<br>
2.next():获取当前指针指向的元素并返回当前的元素,然后指针向下移动一个单位。<br>
3.remove():删除迭代器最后一次返回的元素。
注意:从Object数组中取出的元素只能使用Object类型声明变量接收,如果需要其它的变量类型,需要强制转换。
2.List:
List继承Collection接口,所以Collection中有的方法,在List接口中也适用。
List接口中特有的方法:
1.添加:
add(int index,E element):将指定元素添加到集合中的指定索引值位置上。
addAll(int index,Collection c):将指定集合添加到另一集合中指定索引值的位置上。
2.获取:
get(int index):使用get方法遍历集合中的元素,即获取指定索引值位置的集合中的元素
indexOf(Object o):找出指定元素第一次出现在集合中的索引值
lastIndexOf(Object o):找出指定元素最后一次出现在集合中的索引值
subList(fromIndex,toIndex):指定开始与结束的索引值,截取集合中的元素(在Java中截取都是包头不包尾的,如果想要截取到指定长度的元素,应该将末尾值加1)
3.修改:
set(int index,E element):使用指定的元素替换指定索引值的元素
注意:List接口中特有的方法具备的特点:操作的方法都存在索引值,同时只有List接口下面的集合类才具备索引值。其它接口类下面的集合类都没有索引值。
4.迭代:
listIterator():该方法返回的是一个List接口中特有的迭代器
ListIterator接口中特有的方法:
hasPrevious():判断是否存在上一个元素
previous():获取上一个元素,即当前指针先向上移动一个单位,然后再取出当前指针指向的元素
add(E e):将当前元素插入到当前指针指向的位置上
set(E e):替代迭代器最后一次返回的元素
练习:使用三种方式遍历集合的元素
1.使用get方法遍历
2.使用迭代器正序遍历
3.使用迭代器逆序遍历
package cn.itcast.collelction;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class demo4 {
public static void main(String[] args){
List list=new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
System.out.println("=======使用get方法遍历======");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i)+",");
}
System.out.println("========使用迭代其正序遍历======");
ListIterator it=list.listIterator();//获取迭代器
while(it.hasNext()){
System.out.println(it.next()+",");
}
System.out.println("=========使用迭代逆序遍历=======");
while(it.hasPrevious()){//hasPrevious():判断是否存在上一个元素
System.out.println(it.previous()+",");//previous():获取上一个元素,即当前指针先向上移动一个单位,然后再取出当前指针指向的元素
}
}
}
迭代器:指专门取出集合元素的对象,但是此对象很特殊,不能直接创建对象(new),该对象是以内部类的形式存在每个集合类的内部。因为每个容器都有取出元素的功能。即Iterator是一个对象,它的工作是遍历并选择序列中的对象,它提供一种访问一个容器(container)对象中的各个元素,而又不必暴露该对象内部细节的方法。所以通过迭代器,开发人员不需要了解容器底层的接口,就可以实现对容器的遍历。
迭代器在遍历元素时注意事项:
1.在迭代器(内存模型)迭代元素的过程中,不允许使用集合对象改变集合中的元素的个数,如果需要添加删除只能使用迭代器的方法进行操作。如果使用了集合对象改变集合中元素个数就会出现ConcurrentModificationException异常。
提示:1.使用容器iterator()方法返回一个Iterator,然后通过Iterator的next()方法返回第一个元素。2.使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。3.可以通过remove()方法删除迭代器返回的元素。
3.List的实现类
1.ArrayList
ArrayList特有的方法:
1.ensureCapacity(int minCapacity) 该操作增加ArrayList容量
2.trimToSize()
ArrayList底层维护了一个Object数组实现的(底层是Objec数组写的),所谓动态的是它的大小可变。 特点:查询速度块,增删慢(因为此时如果ArrayList长度不够用的时候需要创建新的ArrayList对象,然后将原来的数据拷贝到新建的对象中,那么这一过程就非常的漫长)
ArrayList何时使用:根据数据特点,如果当前数据查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。比如高校的图书馆,这时候查询的操作比较多,然后增删的操作比较少,此时可以使用ArrayList来操作。
笔试题目:使用ArrayList无参构造函数创建一个对象时,默认的容量是多少?如果长度不够使用时又自增增长多少?
答:ArrayList底层是维护了一个Object数组实现的,使用无参构造函数时,Object数组默认的容量是10,当长度不够用时,自动增长0.5倍。
2.LinkedList
实现原理:使用链表数据结构实现的。特点:查询速度慢,增删快(因为内存地址不连续,所以查询的时候需要遍历集合完之后才能查询到相应的元素的位置)
LinkedList的特有方法:
addFirst():将元素添加到集合的首位置上
addLast():将元素添加到集合的末尾处
getFirst():获取集合中首位置的元素
getLast():获取集合中末尾的元素
removeFirst():删除集合中的首位置元素并返回
removeLast():删除集合中的末尾位置元素并返回
descendingIterator():
push():进栈
pop():出栈
offer():进队列
poll():出队列
机试题目:使用LinkedList实现堆栈数据结构的存储方式与队列的数据结构存储方式
3.Vector(了解) 底层是维护了一个Object的数组实现的,实现与ArrayList一样,但是Vector是线程安全的,操作效率低。(已经被ArrayList取代)
笔试题:说出ArrayList与Vector的区别?
答:相同点:ArrayList与Vector底层都是使用了Object数组实现的。
不同点:1.ArrayList是线程不同步的,操作效率高。Vector是线程同步的,操作效率低。
2.ArrayList是JDK1.2出现的,Vector是jdk1.0出现的。
ArrayList/Vector/LinkedList之间的区别:ArrayList/Vector/LinkedList类均在java.util包中,均为可伸缩数组,即可以动态改变长度的数组。ArrayList和Vector是在内存中开辟一块连续的空间来存储的,由于数据存储是连续的,所以他们支持用序号(下标)来访问元素,同时索引数据的速度比较快,但是在插入元素时需要移动容器中的元素,所以对数据的插入操作执行比较慢。ArrayList与Vector最大的区别是synchronization(同步)的使用,没有一个ArrayList的方法是同步的,而Vector的大部分方法都是直接或间接同步的,所以Vector是线程安全的,而ArrayList是线程不安全的。LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率低下,但是在插入元素时不需要对数据进行移动,所以插入效率较高。此外,LinkedList是非线程安全的容器。
4.Set接口
Set接口实现的集合类特点:无序、不可重复
1.无序:元素添加的顺序与出来的顺序是不一样的
2.不可重复:set集合里面的元素是不可重复的。
1.Set接口下面的特有方法
Set接口下面的实现类:
1.HashSet:底层是使用哈希表来支持的,特点:存取速度快。
实现原理:往HashSet添加元素的时候HashSet会先调用元素的hashCode()方法得到元素的哈希值,然后通过元素的哈希值经过移位运算,就可以算出该元素在哈希表中的存储位置。
情况一:如果算出元素存储的位置目前没有存储元素,那么该元素可以直接存储到该位置。
情况二:如果算出该元素的存储位置目前已经存在有元素,那么会调用该元素的equals()方法与该位置的元素再比较一次,如果equals方法返回的是true,那么该元素与此位置上的元素是重复的,不允许添加,反之可以直接添加。
哈希表的特点:桶式结构(一个空间里面可以装多个元素)
注意:HashCode值在默认情况下是表示的内存地址,但是在引用类的时候类会重写hashCode()方法,所以hashCode值可能会相同。
2.TreeSet:如果元素具备自然顺序特性,那么就按照元素自然顺序的特性进行排序存储
TreeSet注意事项:
1.往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
2.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法中。
3.如果在比较元素的时候,compareTo()方法返回的是0,那么该元素就被视为重复元素,不允许添加(注意:TreeSet与hashCode()、equals()方法是没有任何关系的)
4.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类与没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
5.往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口,在创建TreeSet对象的时候也传入了比较器,那么是以比较器的比较规则优先使用。
自定义比较器:自定义一个类实现Comparator接口,把元素与元素之间的比较规则定义在compare()方法即可。
格式:
class 类名 implements Comparator{
}
推荐使用:比较器(comparator)
TreeSet实现原理:
底层是使用红黑树(二叉树)数据结构实现的。存储规则:左小右大
注意:TreeSet是可以对字符串进行排序的,因为字符串已经实现了Comparable接口。
字符串比较规则:
情况一:可以找到不同字符,比较的就是对应位置上的字符。
情况二:找不到对应不同的字符,比较的就是字符串的长度。
注意:1.eclipse的快捷键:Ctrl+shift+/ 添加多行注释 Ctrl+shift+\ 取消多行注释
5.Map接口
Map中的元素是两个对象,一个对象作为键,一个对象作为值。键是不可以重复的,但是值可以重复。Map与Collection在集合框架中属于并列的存在。Map存储的是键值对。Map接口,表示将键映射到值的对象,一个映射不能包含重复的键,每个键最多能映射到一个值。
1.HashMap 采用哈希表实现,无序。底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。
1.1 LinkedHashMap:该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率
2.TreeMap 可以对键进行排序,底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。
3.Hashtable 底层是哈希表数据结构,线程同步,不可以存入null键,null值。效率较低,被 HashMap 替代
案例:使用HashMap建立学生姓名与年龄之间的映射关系。
import java.util.HashMap;
import java.util.Map;
public class Demo {
public static void main(String[] args) {
//定义一个Map容器对象
Map<String,Integer> map1 = new HashMap<String,Integer>();
map1.put("jack", 20);
map1.put("rose", 18);
map1.put("lucy", 17);
map1.put("java", 25);
System.out.println(map1);
//添加重复的键值(值不同),会返回集合中原有(重复键)的值
System.out.println(map1.put("jack",30));//20
Map<String,Integer> map2 = new HashMap<String,Integer>();
map2.put("Aaron",25);
map2.put("Brian",35);
System.out.println("map2:"+map2);
//从指定的映射中将所有映射关系复制到此映射中
map1.putAll(map2);
System.out.println("map1:"+map1);
//删除
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("jack", 20);
map1.put("rose", 18);
map1.put("lucy", 17);
map1.put("java", 25);
System.out.println(map1);
// 指定key,返回删除的键值对映射的值。
// remove() 该方法删除关联对象,指定key对象
// clear() 清空集合对象
System.out.println("value:" + map1.remove("java"));
map1.clear();
System.out.println("map1:" + map1);
//获取
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("jack", 20);
map1.put("rose", 18);
map1.put("lucy", 17);
map1.put("java", 25);
System.out.println(map1);
// V get(Object key) 通过指定的key对象获取value对象
// int size() 获取容器的大小
System.out.println("value:" + map1.get("jack"));
System.out.println("map.size:" + map1.size());
//判断
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("jack", 20);
map1.put("rose", 18);
map1.put("lucy", 17);
map1.put("java", 25);
System.out.println(map1);
System.out.println("isEmpty:" + map1.isEmpty());//长度为0返回true,否则返回false
System.out.println("containskey:" + map1.containsKey("jack"));//判断集合中是否包含指定key
System.out.println("containsvalues:" + map1.containsValue(100));
}
}
5.1 HashMap
底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class Demo {
public static void main(String[] args){
HashMap<Person,String> hashMap = new HashMap<Person,String>();
hashMap.put(new Person("jack",20),"1001");
hashMap.put(new Person("Aaron",20),"1002");
System.out.println(hashMap);
System.out.println(hashmap.put(new Person("rose",18),"10086"));
Set<Entry<Person,String>> entrySet = new hashMap.entrySet();
Iterator<Entry<Person,String>> it = entrySet.iterator();
while(it.hasNext()) {
Entry<Person,String> next = it.next();
Person key = next.getKey();
String value = next.getValue();
System.out.println(key+"="+value);
}
}
}
class Person {
private String name;
private int age;
Person() {}
public Person(String name,int age){
this.name = name;
this.age = age;
}
//set/get方法
@Override
public int hashCode(){
return this.name.hashCode()+age*37;
}
@Override
public String toString(){
return "PersonName:"+this.name+"age:"+age;
}
}
5.2 TreeMap
注意:Set的元素不可重复,Map的键不可重复,如果存入重复元素如何处理?
1.Set元素重复元素不能存入add方法返回false。
2.Map的重复健将覆盖旧键,将旧值返回。
6.使用Lambda表达式遍历集合
Java8 为Iterable接口新增方法forEach(),用来遍历集合中的元素。
注意:Iterator必须依附Collection对象。